#include "main.h"

using namespace animationagent;

static const Uint32 TICK_INTERVAL = 20;
static Uint32 next_time = 0;
static bool running = true;

#if 0
static SDL_Rect widget_areas[] = {
    {351,28,457,163},
    {461,28,567,163},
    {571,28,677,163},
    {681,28,787,163},
    {791,28,897,163},
    {901,28,1007,163},

    {351,441,457,576},
    {461,441,567,576},
    {571,441,677,576},
    {681,441,787,576},
    {791,441,897,576},
    {901,441,1007,576},
};
#endif
static SDL_Rect widget_areas[] = {
    {351,28,106,135},
    {461,28,106,135},
    {571,28,106,135},
    {681,28,106,135},
    {791,28,106,135},
    {901,28,106,135},

    {351,441,106,135},
    {461,441,106,135},
    {571,441,106,135},
    {681,441,106,135},
    {791,441,106,135},
    {901,441,106,135},
};

Uint32 time_left(void)
{
    Uint32 now;

    now = SDL_GetTicks();
    if(next_time <= now)
        return 0;
    else
        return next_time - now;
}

bool in_rect(int x, int y, SDL_Rect *rect)
{
    if (x >= rect->x && y >= rect->y) {
        if (x < rect->x + rect->w && y < rect->y + rect->h) {
            return true;
        }
    }
    return false;
}

void update_widget(Widget *w, void *param, void *param2)
{
    if (w == NULL || param == NULL) {
        return;
    }
    w->update();
}

void draw_widget(Widget *w, void *param, void *param2)
{
    if (w == NULL || param == NULL) {
        return;
    }
    SDL_Surface *screen = (SDL_Surface *)param;
    w->draw(screen);
}

void GameSpace::draw(SDL_Surface *screen)
{
    Widget::wtree_levelorder_traversal(&layer_background, update_widget, screen, NULL);
    Widget::wtree_levelorder_traversal(&layer_sprite, update_widget, screen, NULL);
    Widget::wtree_levelorder_traversal(&layer_front, update_widget, screen, NULL);
    if (DirtyRect::is_all_dirty()) {
        SDL_FillRect(screen, NULL, 0);
    } else {
        int rect_num = DirtyRect::get_number();
        int i;
        for (i=0; i<rect_num; i++) {
            SDL_FillRect(screen, DirtyRect::get_rect(i), 0);
            //printf("dirty rect,%d: %d, %d, %d, %d\n", i, DirtyRect::get_rect(i)->x, DirtyRect::get_rect(i)->y, DirtyRect::get_rect(i)->w, DirtyRect::get_rect(i)->h);
        }
    }
    Widget::wtree_levelorder_traversal(&layer_background, draw_widget, screen, NULL);
    Widget::wtree_levelorder_traversal(&layer_sprite, draw_widget, screen, NULL);
    Widget::wtree_levelorder_traversal(&layer_front, draw_widget, screen, NULL);
}

void GameSpace::handle_user_event(SDL_Event *e)
{
    const char *objid = (const char *)e->user.data1;
    switch (e->user.code) {
    case ON_MOVE_FINISH:
        printf("'%s' finish move at: %u\n", objid, SDL_GetTicks());
        break;
    case ON_ANIM_FINISH:
        break;
    case ON_FADEALPHA_FINISH:
        break;
    default:
        break;
    }
}

void GameSpace::handle_mousemotion(int x, int y)
{
    int index = -1;
    static SDL_Surface *old_image = NULL;

    for (unsigned int i=0; i<sizeof(widget_areas)/sizeof(SDL_Rect); i++) {
        if (in_rect(x, y, &widget_areas[i])) {
            index = i;
            break;
        }
    }
    Widget *border = static_cast<Widget *>(widgethash.g_hash_table_lookup("border_green"));
    if (index > 5) {
        if (border == NULL) {
            return;
        }
        border->set_allocation(widget_areas[index].x-1, widget_areas[index].y-1);
        // If border now is hidden, we show it
        if (!border->get_visible()) {
            border->set_visible(true);
        }
        Canvas *big_card = static_cast<Canvas *>(widgethash.g_hash_table_lookup("big_card"));
        Text *big_card_desc = static_cast<Text *>(widgethash.g_hash_table_lookup("big_card_desc"));
        if (old_image != render_cards1[index-6]->get_cardimage() || big_card->get_image() == NULL) {
            old_image = render_cards1[index-6]->get_cardimage();
            big_card->set_image(old_image);
            big_card_desc->set_text(core.get_card(core.PLAYER_1, index-6)->get_describe().c_str());
        }
    } else {
        // If border now is visible, we hide it
        if (border->get_visible()) {
            border->set_visible(false);
            Canvas *big_card = static_cast<Canvas *>(widgethash.g_hash_table_lookup("big_card"));
            Text *big_card_desc = static_cast<Text *>(widgethash.g_hash_table_lookup("big_card_desc"));
            big_card->set_image(NULL);
            big_card_desc->set_text(NULL);
        }
    }
}

void GameSpace::handle_event(SDL_Event *e)
{
    unsigned int i = 0;
    switch (e->type) {
    case SDL_QUIT:
        running = false;
        break;
    case SDL_USEREVENT:
        handle_user_event(e);
        break;
    case SDL_MOUSEBUTTONUP:
        break;
    case SDL_MOUSEBUTTONDOWN:
    {
        int index = -1;
        for (i=0; i<sizeof(widget_areas)/sizeof(SDL_Rect); i++) {
            if (in_rect(((SDL_MouseButtonEvent *)e)->x, ((SDL_MouseButtonEvent *)e)->y, &widget_areas[i])) {
                index = i-6;
                break;
            }
        }
        if (index >= 0) {
            play_a_card(GameCore::PLAYER_1, index);
        }
        break;
    }
    case SDL_MOUSEMOTION:
        handle_mousemotion(((SDL_MouseMotionEvent *)e)->x, ((SDL_MouseMotionEvent *)e)->y);
        break;
    default:
        break;
    }
}

void GameSpace::build_stage()
{
    // Set rand seed
    srand ( time(NULL) );

    // Init players
    core.player1->set_hitpoint(20);
    core.player1->set_inc_mana(MAGIC_BLUE, 1);
    core.player1->set_inc_mana(MAGIC_GREEN, 1);
    core.player1->set_inc_mana(MAGIC_RED, 1);
    core.player1->set_manapoint(MAGIC_BLUE, 10);
    core.player1->set_manapoint(MAGIC_GREEN, 10);
    core.player1->set_manapoint(MAGIC_RED, 10);
    *core.player2 = *core.player1;
    core.player1->set_name("Arok");
    core.player2->set_name("Gellidus");

    // Draw cards
    int i=0;
    for (i=0; i<core.MAX_HANDCARD_NUM; i++) {
        if(!core.draw_a_card(core.PLAYER_1, i)) {
            throw "draw card faild";
        }
        if(!core.draw_a_card(core.PLAYER_2, i)){
            throw "draw card faild";
        }
    }
}

GameSpace::GameSpace() :
    core(),
    widgethash(misc::g_str_hash, misc::g_str_equal),
    resourcemanager(),
    actionqueue(),
    layer_background(),
    layer_sprite(),
    layer_front()
{
    gamelua_init(&core);
    //load font
    font = TTF_OpenFont("./font/ukai.ttf", 36);
    if(!font) {
        fprintf(stderr, "TTF_OpenFont: %s\n", TTF_GetError());
        exit(5);
    }

    //load textcollection
    try {
        resourcemanager.load_textcollection("./script/texts.ch");
    } catch ( char *str ) {
        fprintf(stderr, "%s\n", str);
        exit(7);
    } catch ( const char *str) {
        fprintf(stderr, "%s\n", str);
        exit(7);
    }
    //load all image
    try {
        vector<string> filenames = DIROprations::get_all_filename("./images", NULL);
        for (int i = filenames.size() - 1; i >= 0; i--) {
            resourcemanager.load_image(
                DIROprations::build_fullpath("./images", filenames[i]).c_str()
                );
        }
    } catch ( char *str ) {
        fprintf(stderr, "%s\n", str);
        exit(7);
    } catch ( const char *str) {
        fprintf(stderr, "%s\n", str);
        exit(7);
    }

    try {
        //load cards
        core.load_all_cards("./cards");
        //init gamecore
        build_stage();
    } catch ( char *str ) {
        fprintf(stderr, "%s\n", str);
        exit(7);
    } catch ( const char *str) {
        fprintf(stderr, "%s\n", str);
        exit(7);
    }

    //build widget
    try {
        Widget *w = NULL;
        Widget *w2 = NULL;
        int i = 0;
        char objid[256];
        //public
        w = new Canvas(resourcemanager.get_image("bg.jpg"));
        w->set_id("background");
        widgethash.g_hash_table_insert(const_cast<char *>(w->get_id()), w);
        layer_background.wtree_add_child(w);

        w = new Text(font);
        w->set_id("showfps");
        widgethash.g_hash_table_insert(const_cast<char *>(w->get_id()), w);
        layer_front.wtree_add_child(w);

        w = new Canvas();
        w->set_id("big_card");
        w->set_allocation(27, 35);
        widgethash.g_hash_table_insert(const_cast<char *>(w->get_id()), w);
        layer_front.wtree_add_child(w);
        w2 = w;

        w = new Canvas(resourcemanager.get_image("frame_darkness.png"));
        w->set_id("big_card_border");
        w->set_allocation(-17, -25);
        widgethash.g_hash_table_insert(const_cast<char *>(w->get_id()), w);
        w2->wtree_add_child(w);
        w2 = w;

        w = new Text(font);
        w->set_id("big_card_desc");
        w->set_allocation(21, 293);
        widgethash.g_hash_table_insert(const_cast<char *>(w->get_id()), w);
        w2->wtree_add_child(w);
        w2 = NULL;

        w = new Canvas(resourcemanager.get_image("border_green.png"));
        w->set_id("border_green");
        w->set_visible(false);
        widgethash.g_hash_table_insert(const_cast<char *>(w->get_id()), w);
        layer_front.wtree_add_child(w);

        Card *tmpcard = NULL;
        //player1
        for (i=0; i<core.MAX_HANDCARD_NUM; i++) {
            tmpcard = core.get_card(core.PLAYER_1, i);
            w = new render_card(render_card::PLAYER_1, i);
            sprintf(objid, "%s%d", "p1_handcard_", i);
            w->set_id(objid);
            w->set_allocation(widget_areas[i+6]);
            static_cast<render_card *>(w)->set_backimage(resourcemanager.get_image("cardback.jpg"));
            render_cards1.push_back(static_cast<render_card *>(w));

            set_render_card(static_cast<render_card *>(w), tmpcard);
            static_cast<render_card *>(w)->set_show_cardback(false);

            widgethash.g_hash_table_insert(const_cast<char *>(w->get_id()), w);
            layer_sprite.wtree_add_child(w);
        }
        //player2
        for (i=0; i<core.MAX_HANDCARD_NUM; i++) {
            tmpcard = core.get_card(core.PLAYER_2, i);
            w = new render_card(render_card::PLAYER_2, i);
            sprintf(objid, "%s%d", "p2_handcard_", i);
            w->set_id(objid);
            w->set_allocation(widget_areas[i]);
            static_cast<render_card *>(w)->set_backimage(resourcemanager.get_image("cardback.jpg"));
            render_cards2.push_back(static_cast<render_card *>(w));

            set_render_card(static_cast<render_card *>(w), tmpcard);
            static_cast<render_card *>(w)->set_show_cardback(true);

            widgethash.g_hash_table_insert(const_cast<char *>(w->get_id()), w);
            layer_sprite.wtree_add_child(w);
        }

        /*
        //test code
        w = new render_card(render_card::PLAYER_2, 16);
        w->set_id("gaga");
        w->set_allocation(0,0,106,135);
        static_cast<render_card *>(w)->set_backimage(resourcemanager.get_image("cardback.jpg"));
        widgethash.g_hash_table_insert(const_cast<char *>(w->get_id()), w);
        layer_sprite.wtree_add_child(w);
        w->set_movement(new MovementLine(w->get_allocation().x, w->get_allocation().y, 600, 600, 10000));
        */

    } catch ( char *str ) {
        fprintf(stderr, "%s\n", str);
        exit(7);
    } catch ( const char *str) {
        fprintf(stderr, "%s\n", str);
        exit(7);
    }
}

void GameSpace::set_render_card(render_card *w, Card *card)
{
    static_cast<render_card *>(w)->set_cardimage(resourcemanager.get_image(card->get_imagefile().c_str()));
    static_cast<render_card *>(w)->set_cardimage_s(resourcemanager.get_image(card->get_imagefile_s().c_str()));
    // refresh big card
    int x = 0, y = 0;
    SDL_GetMouseState(&x, &y);
    handle_mousemotion(x, y);
}

void GameSpace::add_action_widget_moveto_abus(Widget *widget, Uint32 timestamp, Uint32 duration, Sint16 x, Sint16 y)
{
    add_action_widget_move(widget, ACTION_MOVE, timestamp, duration, x, y);
}

void GameSpace::add_action_widget_moveto_rela(Widget *widget, Uint32 timestamp, Uint32 duration, Sint16 x, Sint16 y)
{
    add_action_widget_move(widget, ACTION_MOVE, timestamp, duration, widget->get_allocation().x + x, widget->get_allocation().y + y);
}

void GameSpace::add_action_widget_putat_abus(Widget *widget, Uint32 timestamp, Uint32 duration, Sint16 x, Sint16 y)
{
    add_action_widget_move(widget, ACTION_SET_POSITION, timestamp, duration, x, y);
}

void GameSpace::add_action_widget_putat_rela(Widget *widget, Uint32 timestamp, Uint32 duration, Sint16 x, Sint16 y)
{
    add_action_widget_move(widget, ACTION_SET_POSITION, timestamp, duration, widget->get_allocation().x + x, widget->get_allocation().y + y);
}

void GameSpace::add_action_widget_move(Widget *widget, int type, Uint32 timestamp, Uint32 duration, Sint16 x, Sint16 y)
{
    ActionNode *node = new ActionNode();
    node->timestamp = timestamp;
    node->type = type;
    node->param.movment.duration = duration;
    node->param.movment.widget = widget;
    node->param.movment.x = x;
    node->param.movment.y = y;

    actionqueue.action_push(node);
}

void GameSpace::add_action_widget_fadein(Widget *widget, Uint32 timestamp, Uint32 duration)
{
    add_action_widget_fade(widget, ACTION_FADE_IN, timestamp, duration);
}

void GameSpace::add_action_widget_fadeout(Widget *widget, Uint32 timestamp, Uint32 duration)
{
    add_action_widget_fade(widget, ACTION_FADE_OUT, timestamp, duration);
}

void GameSpace::add_action_widget_fade(Widget *widget, int type, Uint32 timestamp, Uint32 duration)
{
    ActionNode *node = new ActionNode();
    node->timestamp = timestamp;
    node->type = type;
    node->param.fade.duration = duration;
    node->param.fade.widget = widget;

    actionqueue.action_push(node);
}

void GameSpace::add_action_game(int type, Uint32 timestamp, GameCore::PlayerID p, int index)
{
    ActionNode *node = new ActionNode();
    node->timestamp = timestamp;
    node->type = type;
    node->param.cardaction.player = p;
    node->param.cardaction.index = index;

    actionqueue.action_push(node);
}

void GameSpace::play_a_card(GameCore::PlayerID p, int index)
{
    /*
    core.play_a_card(p, index);
    render_cards1[index]->set_effect(new EffectAlpha(0xff, 0xff, 1000));
    core.draw_a_card(p, index);
    if (p == GameCore::PLAYER_1) {
        set_render_card(render_cards1[index], core.get_card(p, index));
    } else {
        set_render_card(render_cards2[index], core.get_card(p, index));
    }
    */

    Widget *widget = NULL;
    if (p == GameCore::PLAYER_1) {
        widget = render_cards1[index];
    } else {
        widget = render_cards2[index];
    }
    Uint32 timestamp = SDL_GetTicks();

    add_action_game(ACTION_PLAYCARD, timestamp, p, index);
    if (p == GameCore::PLAYER_1) {
        add_action_widget_moveto_rela(widget, timestamp, 600, 0, -200);
    } else {
        add_action_widget_moveto_rela(widget, timestamp, 600, 0, 200);
    }
    add_action_widget_fadeout(widget, timestamp, 600);
    add_action_game(ACTION_DRAWCARD, timestamp+600, p, index);
    if (p == GameCore::PLAYER_1) {
        add_action_widget_putat_rela(widget, timestamp+600, 600, 0, 200);
    } else {
        add_action_widget_putat_rela(widget, timestamp+600, 600, 0, -200);
    }
    if (p == GameCore::PLAYER_1) {
        add_action_widget_moveto_rela(widget, timestamp+600, 600, 0, 0);
    } else {
        add_action_widget_moveto_rela(widget, timestamp+600, 600, 0, 0);
    }
    add_action_widget_fadein(widget, timestamp+600, 600);
}

void GameSpace::handle_action(ActionNode *node)
{
    switch (node->type) {
        case ACTION_MOVE:
        {
            Widget *w = node->param.movment.widget;
            w->set_movement(new MovementLine(w->get_allocation().x, w->get_allocation().y,
                node->param.movment.x, node->param.movment.y, node->param.movment.duration));
            break;
        }
        case ACTION_SET_POSITION:
        {
            Widget *w = node->param.movment.widget;
            w->set_allocation(node->param.movment.x, node->param.movment.y);
            break;
        }
        case ACTION_FADE_IN:
        {
            Widget *w = node->param.fade.widget;
            w->set_effect(new EffectAlpha(0x00, 0xff, node->param.fade.duration));
            break;
        }
        case ACTION_FADE_OUT:
        {
            Widget *w = node->param.fade.widget;
            w->set_effect(new EffectAlpha(0xff, 0x00, node->param.fade.duration));
            break;
        }
        case ACTION_PLAYCARD:
        {
            core.play_a_card(node->param.cardaction.player, node->param.cardaction.index);
            break;
        }
        case ACTION_DRAWCARD:
        {
            core.draw_a_card(node->param.cardaction.player, node->param.cardaction.index);
            if (node->param.cardaction.player == GameCore::PLAYER_1) {
                set_render_card(render_cards1[node->param.cardaction.index], core.get_card(node->param.cardaction.player, node->param.cardaction.index));
            } else {
                set_render_card(render_cards2[node->param.cardaction.index], core.get_card(node->param.cardaction.player, node->param.cardaction.index));
            }
            break;
        }
        case ACTION_DISCARDCARD:
        {
            core.discard_a_card(node->param.cardaction.player, node->param.cardaction.index);
            break;
        }
        default:
            break;
    }
}

void GameSpace::do_action()
{
    while (!actionqueue.empty()) {
        ActionNode *node = actionqueue.action_poll();
        handle_action(node);
        delete node;
    }
}

GameSpace::~GameSpace()
{
    gamelua_close();
    resourcemanager.free_all();
    TTF_CloseFont(font);
}

#if 1
int main (int argc, char *argv[])
{
    SDL_Surface *screen = NULL;
    SDL_Event event;
    Uint32 start_time = 0;
    Uint32 debug_time = 0;

    if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER)==-1)) {
        exit(1);
    }
    atexit(SDL_Quit);

    if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) == -1) {
        fprintf(stderr, "Mix_OpenAudio: %s\n", Mix_GetError());
        exit(2);
    }

    screen = SDL_SetVideoMode(1024, 768, 0, SDL_HWSURFACE | SDL_DOUBLEBUF);
    if ( screen == NULL ) {
        fprintf(stderr, "SDL_SetVideoMode: %s\n", SDL_GetError());
        exit(3);
    }

    if(TTF_Init() == -1) {
        fprintf(stderr, "TTF_Init: %s\n", TTF_GetError());
        exit(4);
    }



    //show loading
    SDL_Surface *surface_loading = IMG_Load("./images/hourglass.png");
    if (surface_loading == NULL) {
        exit(5);
    }
    SDL_BlitSurface(surface_loading, NULL, screen, NULL);
    SDL_Flip(screen);

    GameSpace *gamespace = new GameSpace();


    //finish loading
    SDL_FreeSurface(surface_loading);
    surface_loading = NULL;

    start_time = SDL_GetTicks();
    debug_time = 0;
    //main loop
    while (running) {
        //event loop
        while (SDL_PollEvent(&event)) {
            gamespace->handle_event(&event);
        }

        gamespace->do_action();

        SDL_Delay(time_left());
        /*------------------------*/
        gamespace->draw(screen);
        /*------------------------*/
        SDL_Flip(screen);
        DirtyRect::clear();

        next_time += TICK_INTERVAL;
        next_time = std::max(next_time, SDL_GetTicks());
    }

    delete gamespace;

    SDL_FreeSurface(screen);
    screen = NULL;
    Mix_CloseAudio();
    TTF_Quit();
    /* Shutdown all subsystems */
    SDL_Quit();
    return 0;
}

#else

int main (int argc, char *argv[])
{
    SDL_Surface *screen = NULL;

    if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER)==-1)) {
        exit(1);
    }
    atexit(SDL_Quit);

    screen = SDL_SetVideoMode(1024, 768, 0, SDL_HWSURFACE | SDL_DOUBLEBUF);
    if ( screen == NULL ) {
        fprintf(stderr, "SDL_SetVideoMode: %s\n", SDL_GetError());
        exit(3);
    }

    Uint32 start_time = 0;
    Uint32 end_time = 0;
    int i,j;

    SDL_Rect rect1 = {0,0,512,384};
    SDL_Rect rect2 = {512,384,512,384};

    for (j=0; j<10; j++) {
        fprintf(stdout, "round %d\n", j);

        start_time = SDL_GetTicks();
        for (i=0; i<1000; i++) {
            SDL_SetClipRect(screen, NULL);
            SDL_FillRect(screen, NULL, 0);
        }
        end_time = SDL_GetTicks();
        fprintf(stdout, "step 1 tackes: %d\n", end_time - start_time);

        start_time = SDL_GetTicks();
        for (i=0; i<1000; i++) {
            SDL_SetClipRect(screen, &rect1);
            SDL_FillRect(screen, NULL, 0);
        }
        end_time = SDL_GetTicks();
        fprintf(stdout, "step 2 tackes: %d\n", end_time - start_time);

        start_time = SDL_GetTicks();
        for (i=0; i<1000; i++) {
            SDL_SetClipRect(screen, &rect2);
            SDL_FillRect(screen, NULL, 0);
        }
        end_time = SDL_GetTicks();
        fprintf(stdout, "step 3 tackes: %d\n", end_time - start_time);

        start_time = SDL_GetTicks();
        for (i=0; i<1000; i++) {
            SDL_SetClipRect(screen, &rect1);
            SDL_FillRect(screen, NULL, 0);
            SDL_SetClipRect(screen, &rect2);
            SDL_FillRect(screen, NULL, 0);
        }
        end_time = SDL_GetTicks();
        fprintf(stdout, "step 4 tackes: %d\n", end_time - start_time);
    }

    SDL_FreeSurface(screen);
    screen = NULL;
    /* Shutdown all subsystems */
    SDL_Quit();
    return 0;
}
#endif

